using System;
using System.Runtime.Remoting.Messaging;
using System.Threading;

namespace OhioState.AssetManaged
{
	/// <summary>
	/// This is an abstract utility class for event firing. Different
    /// concrete implementations can provide differing strategies on
    /// how to fire the event.
	/// </summary>
    /// <seealso cref="EventsStrategySerial"/>
    /// <seealso cref="EventsStrategyAsync"/>
    /// <seealso cref="EventsStrategyOneWay"/>
	public abstract class EventsStrategyAbstract
	{
        /// <summary>
        /// Fire the event.
        /// </summary>
        /// <param name="publishedEvent">The event to fire.</param>
        /// <param name="args">An arry of arguments passed into the event.</param>
        abstract public void Fire(Delegate publishedEvent, params object[] args);
	}
    /// <summary>
    /// This is a utility class for event firing using the existing thread. 
    /// This implies that the Fire method will not return until the event
    /// subscribers have all completed their tasks.
    /// </summary>
    /// <seealso cref="EventsStrategyAbstract"/>
    /// <seealso cref="EventsStrategyAsync"/>
    /// <seealso cref="EventsStrategyOneWay"/>
    public class EventsStrategySerial : EventsStrategyAbstract
	{
        /// <summary>
        /// Fire the event.
        /// </summary>
        /// <param name="publishedEvent">The event to fire.</param>
        /// <param name="args">An arry of arguments passed into the event.</param>
        public override void Fire(Delegate publishedEvent, params object[] args)
		{
			if( publishedEvent == null )
				return;
			Delegate[] clientList = publishedEvent.GetInvocationList();
			foreach( Delegate sink in clientList )
			{
				try
				{
					sink.DynamicInvoke( args );
				}
				catch(Exception e)
                {
                    Console.WriteLine(e);
                    //Console.WriteLine(sink.Target);
                    //Delegate[] delegates = publishedEvent.GetInvocationList();
                }
			}
		}
	}
    /// <summary>
    /// This is a utility class for event firing asynchronously. 
    /// This implies that the Fire method will return immediately
    /// and all of the subscribers will be notified on a separate thread.
    /// </summary>
    /// <seealso cref="EventsStrategyAbstract"/>
    /// <seealso cref="EventsStrategySerial"/>
    /// <seealso cref="EventsStrategyOneWay"/>
    public class EventsStrategyAsync : EventsStrategyAbstract
	{
		//
		// Asynchronous event firing with exception catching
		//
		delegate void AsyncFire( Delegate client, object[] args );
        /// <summary>
        /// Fire the event.
        /// </summary>
        /// <param name="publishedEvent">The event to fire.</param>
        /// <param name="args">An arry of arguments passed into the event.</param>
        public override void Fire(Delegate publishedEvent, params object[] args)
		{
			if( publishedEvent == null )
				return;
			Delegate[] clientList = publishedEvent.GetInvocationList();
			IAsyncResult[] asyncResult = new IAsyncResult[clientList.Length];
			System.Threading.WaitHandle[] waitHandles = new WaitHandle[clientList.Length];
			AsyncFire asyncClientCall;
			int i = 0;
			foreach( Delegate sink in clientList )
			{
				try
				{
					asyncClientCall = new AsyncFire( InvokeClient );
					asyncResult[i] = asyncClientCall.BeginInvoke( sink, args, null, null );
					waitHandles[i] = asyncResult[i].AsyncWaitHandle;
					i++;
				}
				catch{}
			}
            try
            {
                //WaitHandle.WaitAll(waitHandles, 1, false);
            }
            catch { }
            //for( int l = 0; l < i; l++ )
            //{
            //    try
            //    {
            //        //asyncResult[l].AsyncWaitHandle.WaitOne( 1, false );
            //    }
            //    catch{}
            //}
		}
		private void InvokeClient( Delegate client, object[] args )
		{
            client.DynamicInvoke( args );
		}
	}
    /// <summary>
    /// This is a utility class for event firing asynchronously. 
    /// This implies that the Fire method will return immediately
    /// and all of the subscribers will be notified on a separate thread.
    /// The OneWay attribute implies that no return values are allowed
    /// in the firing of the event, as well as no exceptions will be caught.
    /// </summary>
    /// <seealso cref="EventsStrategyAbstract"/>
    /// <seealso cref="EventsStrategySerial"/>
    /// <seealso cref="EventsStrategyAsync"/>
    public class EventsStrategyOneWay : EventsStrategyAbstract
	{
		//
		// Asynchronous event firing without exception or return values
		// Fire the event and move on (fire and forget)
        //
        delegate void OneWayFire(Delegate client, object[] args);
        /// <summary>
        /// Fire the event.
        /// </summary>
        /// <param name="publishedEvent">The event to fire.</param>
        /// <param name="args">An arry of arguments passed into the event.</param>
		public override void Fire( Delegate publishedEvent, params object[] args )
		{
			if( publishedEvent == null )
				return;
			Delegate[] clientList = publishedEvent.GetInvocationList();
			OneWayFire oneWayClientCall;
			foreach( Delegate sink in clientList )
			{
				//
				// No need for try catch blocks with OneWay invocations
				//
				oneWayClientCall = new OneWayFire( InvokeClientOneWay );
                oneWayClientCall.BeginInvoke(sink, args, null, null);
			}
		}
		[OneWay()]
		private void InvokeClientOneWay( Delegate client, object[] args )
		{
                client.DynamicInvoke(args);
		}
	}
}
